%%%-------------------------------------------------------------------
%%% File    : svc_vms.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created : 28 Apr 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(svc_vms).

-include("../include/vms.hrl"). 

-behaviour(gen_server).
%%-------------------------------------------------------------------
%% API
%%-------------------------------------------------------------------
-export([start_link/2,
		 register_factory/1,
		 create_vect/3,
		 get_vect/1]).
-export([test/1]).
%%-------------------------------------------------------------------
%% gen_server callbacks
%%-------------------------------------------------------------------
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).
%%-------------------------------------------------------------------
-define(SERVER,?MODULE).
-define(EXIT(Err),{'EXIT',Err}).

-record(state, {}).

%%-------------------------------------------------------------------

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Factories,Options) ->
	io:format("svc_vms:start_link~n"),
    gen_server:start_link({local, ?SERVER}, ?MODULE, Factories, Options).

register_factory(FactoryDescr) ->
	gen_server:call(?SERVER, {register_factory,FactoryDescr},infinity).
create_vect(Name,VectType,TailArgs) ->
	gen_server:call(?SERVER, {create_vect,Name,VectType,TailArgs},infinity).
get_vect(Name) ->
	gen_server:call(?SERVER, {get_vect,Name},infinity).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init(Factories) ->
	io:format("svc_vms:init~n"),
	init_mnesia(svc_cms:get_randezvouse()),
	init_vms_info(Factories),
	io:format("svc_vms:init 1~n"),
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(Request, _From, State) ->
    case (catch call(Request)) of
		?EXIT(Reason) ->
			{reply, {error,Reason}, State};
		Reply ->
			{reply, Reply, State}
	end.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%%% Call functions
%%--------------------------------------------------------------------
call({register_factory,FactoryDescr}) ->
	sync_transaction(
	  fun() ->
			  case mnesia:read({?FACTORY_TAG,FactoryDescr#?FACTORY_TAG.type}) of
				  [ExistingFactory] ->
					  exit({vect_factory_already_exists,ExistingFactory});
				  [] ->
					  mnesia:write(FactoryDescr#?FACTORY_TAG{credate=now()})
			  end
	  end);
call({create_vect,Name,VectType,TailArgs}) ->
	Factory=sync_transaction(
			  fun() ->
					  [F]=mnesia:read({?FACTORY_TAG,VectType}),F
			  end),
	Mod=Factory#?FACTORY_TAG.fact_module,
	Fun=Factory#?FACTORY_TAG.method,
	DefArgs=Factory#?FACTORY_TAG.def_arg_list,
	case apply(Mod,Fun,Args=[Name|
								 case TailArgs of
									 [] ->
										 DefArgs;
									 _ ->
										 TailArgs
							 end
							]) of
		MVect when is_record(MVect,?MANAGED_VECTOR_TAG) ->
			sync_transaction(
			  	fun() ->
					  mnesia:write(MVect#?MANAGED_VECTOR_TAG{type=VectType})
				end);
		BadMVect ->
			exit({bad_create_method,Factory,BadMVect,Args})
	end;
call({get_vect,Name}) ->
	sync_transaction(
	  fun() ->
			   case mnesia:read({?MANAGED_VECTOR_TAG,Name}) of
				   [] ->
					   exit({vect_not_exist,Name});
				   [MVect] ->
					   Mod=MVect#?MANAGED_VECTOR_TAG.vect_module,
					   Args=MVect#?MANAGED_VECTOR_TAG.new_arg_list,
					   apply(Mod,new,Args)
			   end
	  end).

sync_transaction(Fun) ->
	case mnesia:sync_transaction(Fun) of
		{atomic,Result} ->
			Result;
		{aborted,Reason} ->
			exit(Reason)
	end.


%%--------------------------------------------------------------------
%%% Helper functions
%%--------------------------------------------------------------------
init_mnesia(RandNode)->
	ok=init_table(?MANAGED_VECTOR_TAG,
				  [
				   {disc_copies, [RandNode]},
				   %{index,[#ring.node]},
				   {type, ordered_set},
				   {attributes, record_info(fields, ?MANAGED_VECTOR_TAG)}],
				  RandNode),
	ok=init_table(?FACTORY_TAG,
				  [
				   {disc_copies, [RandNode]},
				   %{index,[#ring.node]},
				   {type, ordered_set},
				   {attributes, record_info(fields, ?FACTORY_TAG)}],
				  RandNode),
	ok=wait_for_tables(start,[?MANAGED_VECTOR_TAG,?FACTORY_TAG]).

init_table(TableName,TabDef,RandNode) ->
	case node() of
		nonode@nohost -> exit(node_is_not_alive);
		RandNode ->
			case mnesia:create_table(TableName,
									 TabDef) of
				{atomic,ok} -> ok;
				{aborted,{already_exists,TableName}}->ok;
				Err -> exit(Err)
			end;
		Node ->
			case mnesia:add_table_copy(TableName, Node, disc_copies) of
				{aborted,{already_exists,TableName,_}} -> ok;
				{atomic,ok} -> ok
			end
	end.


init_vms_info(FactoryList) ->
	[
	 begin
	 	case (catch call({register_factory,FactoryDescr})) of
			ok ->
				error_logger:info_msg("VMS create factory: type=~p, result=~p~n", [FactoryDescr#?FACTORY_TAG.type,created]);
			?EXIT({vect_factory_already_exists,_}) ->
				error_logger:info_msg("VMS create factory: type=~p, result=~p~n", [FactoryDescr#?FACTORY_TAG.type,exists]);
			Err ->
				error_logger:err_msg("VMS create factory: type=~p, result=~p~n", [FactoryDescr#?FACTORY_TAG.type,Err])
		end
	 end
	||FactoryDescr<-FactoryList].

wait_for_tables(ok,TabList) ->
	io:format("Tables ~p are ready.~n",[TabList]),ok;
wait_for_tables({timeout,TabList},TabList) ->
	io:format("Tables ~p still not ready...~n",[TabList]),
	wait_for_tables(start,TabList);
wait_for_tables({error,_Reason}=Err,_TabList) -> exit(Err);
wait_for_tables(start,TabList) ->
	io:format("Wait tables ~p become ready...~n",[TabList]),
	wait_for_tables(mnesia:wait_for_tables(TabList, 1*1000),TabList).
%%--------------------------------------------------------------------
%%% Test functions
%%--------------------------------------------------------------------
test(register) ->
	svc_vms:register_factory(
	  #?FACTORY_TAG{
		 type=rdf_psog,
		 fact_module=rdf_vect_factory,
		 method=create,
		 def_arg_list=[quad_psog,4],
		 comment="Creates a vector for quad store partitioned by predicate.\nNumber of fragments for each predicate table is 4."
	}),
	create_vect(ppp,rdf_psog,[]).
